/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package RadioProp;

import PathModel.*;

/**
 *
 * @author Alwyn
 */

public class PropagationCalcs1546 {

    public static final int LAND=0;
    public static final int COLD_SEA=1;
    public static final int WARM_SEA=2;

    private RefractivityMap refract=new RefractivityMap("c:");
    private PropCurves propCurves=new PropCurves("c:");

    private double dFrequency;
    private double dLocationPercentage;
    private double dSignalPercentage;
    private boolean bIsOutdoor;

    public static enum TerminalClutter {
        OPEN_RURAL_WATER, SUBURBAN, URBAN_TREES_FOREST, DENSE_URBAN
    }

    private class ProfileParameters
    {
        public double d;    // Great circle path distance in km
        public double dlt;  // Distance from transmitter to its horizon in km
        public double dlr;  // Distance from the receiver to its horizon in km
        public double thetaT;   // Transmit elevation angle in mrad
        public double thetaR;   // Receive elevation angle in mrad
        public double theta;    // Path angluar distance in mrad
        public double hts;      // Height of the transmit antenna above mean sea level in m
        public double hrs;      // Height od the receive antenna above mean sea level in m
        public double htc;  // max of hts or clutter height above mean sea level at transmitter
        public double hrc;  // max of hrs or clutter height above mean sea level at receiver
        public double hte;  // Effective height of transmitter antenna above terrain
        public double hre;  // Effective height of receiver antenna above terrain
        public double db;   // Aggragate length of the path sections over sea water in km
        public double omega;    // Fraction of the total path over water
    }

    private ProfileParameters profileParam;

    public PropagationCalcs1546()
    {
        dFrequency=100; //MHz
        dLocationPercentage=50; // %
        dSignalPercentage=50; // %
        bIsOutdoor=true;
    }

    /**
     *
     * Calculates the basic transmission loss
     *
     * @param E the field strength in dB-uV/m for 1kW erp
     * @param f the frequency in MHz
     * @return basic transmission loss in dB
     * */
    public static double Lb(double E, double f)
    {
        return 139.3-E+20*Math.log10(f);
    }

    /**
     *
     * An approximation of the 0.6 Fresnel clearance path length.
     *
     * @param h1 the height of antenna 1 in metres
     * @param h2 the height of antenna 2 in metres
     * @param f the frequency in MHz
     * @return path length in km.
     * */
    public static double D06(double h1, double h2, double f)
    {
        if(h1<0)
            h1=0;

        double Df=0.0000389*f*h1*h2;
        double Dh=4.1*(Math.sqrt(h1)+Math.sqrt(h2));

        double ret=(Df*Dh)/(Df+Dh);

        if(ret<0.001)
            ret=0.001;

        return ret;
    }

    /**
     * Calculates the maximum field strength of a path
     *
     * @param d distance between transmitter and receiver in km
     * @param percent percentage of the time needed in % (not decimal e.g 0.5)
     * @param surface the type of surface the signal is travelling over
     * @return the maximum field strength in dB-uV/m for 1kW transmitter.
     */
    public static double maxFieldStrength(double d, double percent, int surface)
    {
        double Efs=106.9-20*Math.log10(d);

        if(surface==LAND)
            return Efs;

        double Ese=2.38*(1-Math.exp(-d/8.94))*Math.log10(50/percent);

        return Efs+Ese;
    }

    /**
     * Calculates the field strength for sea paths with frequency less than 100MHz.
     * 
     * @param f frequency in MHz
     * @param h1 transmitter height in metres
     * @param d distance between transmitter and receiver in km
     * @param percent percentage required in % (not decimal, e.g. 0.5)
     * @return the field strength in dB-uV/m for 1kW transmitter
     */
    private double sea100MHz(double f, double h1, double d, double percent)
    {
        double df=D06(f,h1,10);

        if(d<=df)
            return maxFieldStrength(d,percent,COLD_SEA);

        double Edf=maxFieldStrength(df,percent,COLD_SEA);
        double d600=D06(600,h1,10);
        double Ed600=propCurves.getFieldStrength(d600, h1, percent, PropCurves.COLD_SEA);

        return Edf+(Ed600-Edf)*Math.log10(d/df)/Math.log10(d600/df);
    }

    public double calculateFieldStrength(PathProfile p)
    {
        //if()
        return 0;
    }

    private void fillProfileParameters(PathProfile p)
    {
        PathPoint tx=p.getTransmitterPathPoint();
        PathPoint rx=p.getReceiverPathPoint();

        profileParam.d=rx.dDistance; // Great circle distance

        
    }

    private double fieldStrength1kW()
    {
        double Lb0p=0,Lbc=0,Lloc=0, sigmaLoc=0;
        
        if(bIsOutdoor)
        {
            Lloc=0;
        }
        else
        {
            double [] building=buildingEntryLoss();
            Lloc=building[0];
        }
        
        double Lb=Math.max(Lb0p,
                Lbc + Lloc - MathsHelper.invNormalDist(dLocationPercentage/100.0)*sigmaLoc);
        
        return 199.36+20*Math.log10(dFrequency/1000.0)-Lb;
    }

    private double terminalClutterLoss(double antHeight, TerminalClutter type)
    {
        double R=0;

        if(antHeight>=getTerminalClutterHeight(type))
            return 0;

        if(type==TerminalClutter.OPEN_RURAL_WATER)
        {
            double Kh2=21.8+6.2*Math.log10(dFrequency/1000.0); // is in MHz
            return (-Kh2*Math.log10(antHeight/10.0));
        }

        double hdiff=getTerminalClutterHeight(type)-antHeight;
        double thetaClut=Math.atan2(hdiff, 27)*180.0/Math.PI; // in degrees
        double Knu=0.342*Math.sqrt(dFrequency/1000.0); // Frequency needs to be in GHz

        return knifeEdgeDiffractionLoss(Knu*Math.sqrt(hdiff*thetaClut))-6.03;
    }

    private double getTerminalClutterHeight(TerminalClutter type)
    {
        switch(type)
        {
            case OPEN_RURAL_WATER:
            case SUBURBAN:
                return 10.0;
            case URBAN_TREES_FOREST:
                return 15.0;
            case DENSE_URBAN:
                return 20.0;
        }
        
        return 0;
    }

    private double knifeEdgeDiffractionLoss(double v)
    {
        if(v<=-0.78)
            return 0;

        return 6.9+20*Math.log10(Math.sqrt((v-0.1)*(v-1)+1)+v-0.1);
    }

    private double [] buildingEntryLoss()
    {
        // index 0 is median, index 1 is standard dev.

        if(dFrequency<=200)
            return(new double[]{9,3});

        if(dFrequency>=600)
            return(new double[]{11,6});

        double m=MathsHelper.linInterpolate(dFrequency, 200, 600, 9, 11);
        double s=MathsHelper.linInterpolate(dFrequency, 200, 600, 3, 6);

        return(new double[]{m,s});
    }
}
